package com.heima.wemedia.service.impl;
import java.util.*;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.wemedia.WemediaContans;
import com.heima.common.exception.CustomException;
import com.heima.common.message.ArticleForEsConstants;
import com.heima.common.message.NewsAutoScanConstants;
import com.heima.common.message.WmNewsMessageConstants;
import com.heima.model.admin.dtos.NewsAuthDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.dtos.WmNewsDto;
import com.heima.model.media.dtos.WmNewsPageReqDto;
import com.heima.model.media.pojos.WmMaterial;
import com.heima.model.media.pojos.WmNews;
import com.heima.model.media.pojos.WmNewsMaterial;
import com.heima.model.media.pojos.WmUser;
import com.heima.model.media.vo.WmNewsVo;
import com.heima.utils.threadlocal.WmThreadLocalUtils;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.stream.Collectors;

/**
 * @作者 itcast
 * @创建日期 2021/1/28 9:11
 **/
@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Value("${fdfs.url}")
    String fileServerUrl;
    @Override
    public ResponseResult findList(WmNewsPageReqDto dto) {
        // 1. 检查参数
        if(dto == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();
        //    检查登录
        WmUser user = WmThreadLocalUtils.getUser();
        if(user == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 2. 封装条件
        // 2.1  分页条件
        Page<WmNews> pageRequest = new Page<>(dto.getPage(), dto.getSize());

        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = Wrappers.<WmNews>lambdaQuery();
        // 2.2  检查状态条件
        if(dto.getStatus() !=null){
            lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
        }
        // 2.3  检查关键字条件  模糊查询
        if(StringUtils.isNotBlank(dto.getKeyword())){
            lambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());
        }
        // 2.4  检查频道条件
        if(dto.getChannelId() !=null){
            lambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        // 2.5  检查发布日期 时间条件
        if(dto.getBeginPubDate() !=null){
            lambdaQueryWrapper.ge(WmNews::getPublishTime,dto.getBeginPubDate());
        }
        if(dto.getEndPubDate() !=null){
            lambdaQueryWrapper.le(WmNews::getPublishTime,dto.getEndPubDate());
        }
        // 2.6  检查登录用户ID条件
        lambdaQueryWrapper.eq(WmNews::getUserId,user.getId());
        // 2.7  按照文章的发布时间来排序
        lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
        // 3. 执行查询  封装结果
        IPage<WmNews> pageResult = page(pageRequest, lambdaQueryWrapper);
        PageResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),pageResult.getTotal());
        responseResult.setHost(fileServerUrl);
        responseResult.setData(pageResult.getRecords());
        return responseResult;
    }

    @Autowired
    KafkaTemplate<String,String> kafkaTemplate;

    @Transactional
    @Override
    public ResponseResult summitNews(WmNewsDto dto) {
        // 1. 检查 并 封装参数
        // 1.1 检查文章内容 状态 用户是否登录
        if(dto == null || StringUtils.isEmpty(dto.getContent())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if(!checkNewsStatus(dto.getStatus())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章状态错误");
        }
        WmUser user = WmThreadLocalUtils.getUser();
        if(user==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 1.2 将dto的内容转化为wmNews实体对象
        WmNews wmNews = new WmNews();
        // 将dto的属性拷贝到wmNews中
        BeanUtils.copyProperties(dto,wmNews);
        // 修改文章详情  替换掉前缀路径
        wmNews.setContent(dto.getContent().replaceAll(fileServerUrl,""));
        // 1.3 如果dto中type为自动的话，需要暂时将type设置为null
        if(WemediaContans.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            wmNews.setType(null);
        }
        // 1.4 将dto中的images封面图片集合 转化为字符串，多个图片用逗号拼接
        //  [ http://ip/图片路径,http://ip/图片路径,http://ip/图片路径,http://ip/图片路径  ]
        List<String> coverImages = dto.getImages(); // 封面图片列表
        if(coverImages!=null && !coverImages.isEmpty()){
            coverImages = coverImages.stream()
                    .map(imgUrl -> imgUrl.replace(fileServerUrl, "").trim())
                    .collect(Collectors.toList());
            // 将集合中的每一个元素 都用 逗号拼接起来  设置成图片封面字符串
            // [ http://ip/图片路径1,http://ip/图片路径2,http://ip/图片路径,http://ip/图片路径3  ]
            // ==>图片路径1,图片路径2,图片路径3
            wmNews.setImages(StringUtils.join(coverImages,","));
        }
        // 2. 保存或修改文章( 根据id是否存在)
        wmNews.setUserId(user.getId());
        saveOrUpdateWmNews(wmNews);
        // 3. 保存文章中内容涉及的素材 和 封面中涉及的素材 它们的关联关系
        // 3.1 先抽取出内容部分所涉及的图片路径url 集合
        List<String> imageList = ectractUrlInfo(dto.getContent());
        // 3.2 如果不是草稿，设置抽取图片与素材关联关系
        if(!WmNews.Status.NORMAL.getCode().equals(dto.getStatus())&& imageList!=null && !imageList.isEmpty()){
            // 保存内容和素材的关联关系
            saveRelativeInfo(imageList,wmNews.getId(),WemediaContans.WM_CONTENT_REFERENCE);
        }
        // 3.3 如果不是草稿, 设置封面图片与素材的关联关系
        //  如果  dto==Type为自动类型 ， 需要自动生成封面图片
        if(imageList!=null&&!imageList.isEmpty()&&WemediaContans.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            coverImages = autoSetCoverImages(wmNews,imageList);
        }
        if(!WmNews.Status.NORMAL.getCode().equals(dto.getStatus())&&coverImages!=null&&!coverImages.isEmpty()){
            saveRelativeInfo(coverImages,wmNews.getId(),WemediaContans.WM_COVER_REFERENCE);// 0 内容  1 封面
        }
        if(wmNews.getStatus()==1){
            kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, JSON.toJSONString(wmNews.getId()));
        }
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult findWmNewsById(Integer id) {
        // 1. 检查参数
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 根据ID查询文章
        WmNews wmNews = getById(id);
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // [{"type":"image","value":"图片" } ]  给文章详情部分添加图片前缀路径
        List<Map> mapList = JSONArray.parseArray(wmNews.getContent(), Map.class);
        mapList = mapList.stream().map(m->{
           if("image".equals(m.get("type"))){
              String imgUrl =  (String)m.get("value"); // group1/M00/00/00/sxx.jpg
               m.put("value",fileServerUrl + imgUrl);
           }
            return m;
        }).collect(Collectors.toList());
        wmNews.setContent(JSON.toJSONString(mapList));
        // 3. 返回结果
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(fileServerUrl);
        return responseResult;
    }

    @Override
    public ResponseResult delNews(Integer id) {
        // 1. 检查参数
        if(id==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 根据文章id , 当前登录用户ID 查询文章数据
        WmNews news = getOne(
                Wrappers.<WmNews>lambdaQuery()
                        .eq(WmNews::getId,id)
                        .eq(WmNews::getUserId,WmThreadLocalUtils.getUser().getId())
        );
        if(news == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 3. 如果status为发布 并且 enable为上架 不能删除
        if(WmNews.Status.PUBLISHED.equals(news.getStatus())&&news.getEnable()==1){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"请先下架该文章");
        }
        // 4. 删除文章和素材的关联关系
        wmNewsMaterialMapper.delete(
                Wrappers.<WmNewsMaterial>lambdaQuery()
                        .eq(WmNewsMaterial::getNewsId,id)
        );
        // 5. 删除文章数据
        removeById(id);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        // 1. 检查参数    enable (0   1)
        if(dto == null
                || (dto.getEnable()!=0 && dto.getEnable()!=1)
                || dto.getId() == null
        ){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 获取文章 ( status == 9 只有发布的文章才可以上下架)
        WmNews wmNews = getOne(Wrappers.<WmNews>lambdaQuery()
                .eq(WmNews::getId, dto.getId())
                .eq(WmNews::getUserId, WmThreadLocalUtils.getUser().getId())
        );
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (!WmNews.Status.PUBLISHED.getCode().equals(wmNews.getStatus())) {
            //TODO 使用Kafka给文章微服务发送消息
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"只有发布状态的文章才可以上下架");
        }
        // 3. 修改文章
        boolean flag = update(
                Wrappers.<WmNews>lambdaUpdate()
                        .set(WmNews::getEnable,dto.getEnable())
                        .eq(WmNews::getId,dto.getId())
        );
        if(flag&&wmNews.getArticleId()!=null){
            //  articleId     enable
            Map<String,Object> map = new HashMap<>();
            map.put("articleId",wmNews.getArticleId());
            map.put("enable",dto.getEnable());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(map));
            if(dto.getEnable() == 0){
                // 下架  索引库删除文章
                kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_DELETE,String.valueOf(wmNews.getArticleId()));
            }else {
                // 上架架  索引库添加文章
                kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,String.valueOf(wmNews.getArticleId()));
            }
        }
        // 4. 返回响应
        return ResponseResult.okResult();
    }

    /**
     * 查询状态为 4 或 8 并且 发布时间小于等于当前时间
     * @return
     */
    @Override
    public List<Integer> findRelease() {
        // 1. 构建查询条件
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = Wrappers.<WmNews>lambdaQuery();
        // 查询 发布时间小于等于当前时间 并且 (状态 为4 或 8)
        // where publish_time <= NOW() and  (`status` = 4 or `status` = 8)
        lambdaQueryWrapper.le(WmNews::getPublishTime,new Date()).and(w -> w.eq(WmNews::getStatus,4).or().eq(WmNews::getStatus,8));
        log.info("查询待发布文章的sql条件语句==> {}",lambdaQueryWrapper.getSqlSegment());
        // 2. 执行查询
        List<WmNews> list = list(lambdaQueryWrapper);
        // 3. 获取新闻ID列表
        List<Integer> newsIdList = list.stream().map(WmNews::getId).collect(Collectors.toList());
        return newsIdList;
    }
    @Autowired
    WmNewsMapper wmNewsMapper;

    @Override
    public PageResponseResult findList(NewsAuthDto dto) {
        // dto 分页参数   title
        dto.checkParam();
        Integer page = dto.getPage();
        Integer size = dto.getSize();
        dto.setPage((page-1)*size);
        // 补充title参数
        String title = dto.getTitle();
        if(StringUtils.isNotBlank(title)){
            dto.setTitle("%"+title+"%");
        }
        // 得到wmnews的分页数据
        List<WmNewsVo> listAndPage = wmNewsMapper.findListAndPage(dto);
        long listCount = wmNewsMapper.findListCount(dto);
        // 封装结果并返回
        PageResponseResult responseResult = new PageResponseResult(page,size,listCount);
        responseResult.setData(listAndPage);
        return responseResult;
    }
    @Override
    public WmNewsVo findWmNewsVo(Integer id) {
        return wmNewsMapper.findWmNewsVo(id);
    }

    /**
     * 根据内容中的图片列表
     * 自动生成封面图片列表
     * @param wmNews
     * @param imageList  内容中图片列表
     * @return
     */
    private List<String> autoSetCoverImages(WmNews wmNews, List<String> imageList) {
        int size = imageList.size(); // 内容图片数量
        List<String> coverImages = new ArrayList<>();
        if(size>2){
            // 1. 图片的数量是否大于2  将type类型设置为多图   截图图片列表中的前 3张图片作为封面图片
            coverImages = imageList.stream().limit(3).collect(Collectors.toList());
            wmNews.setType(WemediaContans.WM_NEWS_MANY_IMAGE);
        }else if(size>0 && size<=2){  // [1 2 3 4]
            // 2. 图片的数量是否大于0  <=2   将type类型设置为单图   截图图片列表中的前1张图片作为封面图片
            coverImages = imageList.stream().limit(1).collect(Collectors.toList());
            wmNews.setType(WemediaContans.WM_NEWS_SINGLE_IMAGE);
        }else {
            // 3. 图片的数量为0     将type类型设置为无图
            wmNews.setType(WemediaContans.WM_NEWS_NONE_IMAGE);
        }
        if(!coverImages.isEmpty()){
            wmNews.setImages(StringUtils.join(coverImages,","));
        }
        updateById(wmNews); // 修改文章
        // 返回封面图片集合  用于设置关联关系
        return coverImages;
    }

    @Autowired
    WmMaterialMapper wmMaterialMapper;
    @Autowired
    WmNewsMaterialMapper wmNewsMaterialMapper;
    /**
     * 保存 文章图片 和 素材的关联关系
     * @param imageList  图片列表
     * @param newsId 文章ID
     * @param type 引用类型 0 内容  1 封面
     */
    private void saveRelativeInfo(List<String> imageList, Integer newsId, Short type) {
        //1 . 根据  用户ID  ， 根据imageList (url)  查询关联素材   in(url,url,url)    2素材
        // select * from wm_material where url in (url,url,url) and userId = 登录用户ID
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(
                Wrappers.<WmMaterial>lambdaQuery()
                        .in(WmMaterial::getUrl, imageList)
                        .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
        );
        if(wmMaterials==null || wmMaterials.isEmpty()){
            throw new CustomException(ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"关联素材不存在"));
        }
        //2 . 将素材的信息转化为map集合  List<WmMaterial> =>  url作为key   id作为value 封装成一个map集合
        Map<String, Integer> urlAndIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        //3. 判断所有图片在素材中是否存在
        // （遍历图片列表，判断每一张图片在map集合中是否存在  如果不存在直接抛异常 如果存在取出素材ID）
        List<Integer> materialIdList = new ArrayList<>();
        for (int i = 0; i < imageList.size(); i++) {
            String currentImg = imageList.get(i);
            if(!urlAndIdMap.containsKey(currentImg)){
                throw new CustomException(ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"关联素材不存在"));
            }
            materialIdList.add(urlAndIdMap.get(currentImg));
        }
        //4. 根据素材ID 文章ID 引用类型 将关联数据保存到数据库
        // materialIdList= [ 1  3    5   6 7 8 9 10]
        wmNewsMaterialMapper.saveRelationsByContent(materialIdList,newsId,type);// 批量保存关联关系
//        for (int i = 0; i < materialIdList.size(); i++) {
//            Integer materialId = materialIdList.get(i);
//            WmNewsMaterial wmNewsMaterial = new WmNewsMaterial();
//            wmNewsMaterial.setMaterialId(materialId);
//            wmNewsMaterial.setNewsId(newsId);
//            wmNewsMaterial.setType(type);
//            wmNewsMaterial.setOrd((short)i);
//            wmNewsMaterialMapper.insert(wmNewsMaterial);
//        }
        
    }

    /**
     * 抽取文章内容 涉及的图片路径
     * @param content 文章内容  str = "[ {type: text,value:"文本内容"  },{type:image,value:"图片路径"} ]"
     * @return
     */
    private List<String> ectractUrlInfo(String content) {
        // 1. 将内容json字符串转化为map集合
        List<Map> mapList = JSONArray.parseArray(content, Map.class);
        List<String> imageList = new ArrayList<>();
        for (Map map : mapList) {
            String type = (String)map.get("type");
            if(WemediaContans.WM_NEWS_TYPE_IMAGE.equals(type)){
                String imgUrl = (String) map.get("value");
                imageList.add(imgUrl.replace(fileServerUrl,"").trim());
            }
        }
        return imageList;
    }
    private void saveOrUpdateWmNews(WmNews wmNews) {
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable((short)1); // 上架
        if(wmNews.getId()==null){
            // id为空新增该文章
            save(wmNews);
        }else {
            // id不为空代表修改
            // 删除该文章与素材的关联关系
            wmNewsMaterialMapper.delete(
                    Wrappers.<WmNewsMaterial>lambdaQuery()
                            .eq(WmNewsMaterial::getNewsId,wmNews.getId())
            );
            updateById(wmNews);
        }
    }

    /**
     * 检查状态  不能为null 必须是 0 或 1
     * @param status
     * @return
     */
    private boolean checkNewsStatus(Short status) {
        if(status!=null && (WmNews.Status.NORMAL.getCode().equals(status)||WmNews.Status.SUBMIT.getCode().equals(status)) ){
            return true;
        }
        return false;
    }
}
